package code.lucamarrocco.rest.matchers;

import static code.lucamarrocco.rest.Cases.*;
import static code.lucamarrocco.rest.RestfulProtocolMather.*;
import static code.lucamarrocco.rest.Signatures.*;

import java.lang.reflect.*;

import org.hamcrest.*;

import code.lucamarrocco.rest.*;

public class MethodFor extends BaseMatcher<RestfulProtocol> {
	@Factory
	public static Matcher methodFor(RestfulProtocol protocol, String uri) {
		return new MethodFor(protocol, uri);
	}

	private String uri;

	private MethodFor(RestfulProtocol protocol, String uri) {
		this.protocol = protocol;
		this.uri = uri;
		this.matcher = matcher(protocol);
	}

	public void describeTo(Description description) {
		description.appendText(error);
	}

	private String path;

	private RestfulProtocol protocol = new RestfulProtocol();

	private RestfulProtocolMather matcher = new RestfulProtocolMather();

	private boolean doesntExist(Object object) {
		return object == null;
	}

	private String actionpath(String path, RestfulMethod method) {
		return path + "." + asPascalCase(method.getTopic()) + "Action";
	}

	private Class actionClass(RestfulMethod method) {
		if (method == null) return null;
		String actionpath = actionpath(path, method);
		try {
			return getClass().getClassLoader().loadClass(actionpath);
		} catch (ClassNotFoundException e) {
			return null;
		}
	}

	private Method method(Class action, RestfulMethod method) {
		if (action == null) return null;
		if (method == null) return null;

		String name = simpleSignatureOf(method);

		try {
			return action.getMethod(name, new Class[0]);
		} catch (Exception e) {
			return null;
		}
	}

	private String error;

	private boolean error(String error) {
		this.error = error;
		return false;
	}

	public boolean matches(Object item) {
		this.path = (String) item;
		RestfulMethod restMethod = matcher.method(uri);
		Class klass = actionClass(restMethod);
		Method method = method(klass, restMethod);
		if (doesntExist(restMethod)) return error("protocol MAY define rule for " + uri);
		if (doesntExist(klass)) return error("you MAY define " + actionpath(path, restMethod));
		if (doesntExist(method)) return error("you MAY define " + simpleSignatureOf(restMethod) + " into " + actionpath(path, restMethod));
		return true;
	}

	public String signatureOf(String uri) {
		RestfulMethod restmethod = matcher.method(uri);
		if (doesntExist(restmethod)) return "";
		return Signatures.topicSignatureOf(restmethod);
	}
}